package org.jeecg.modules.system.util;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.util.StringUtils;
import org.springframework.core.io.ClassPathResource;
import sun.font.FontDesignMetrics;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

@Slf4j
public class QRCodeUtil {
    private static final int QRCODE_SIZE = 280; // 二维码尺寸，宽度和高度均是320
    private static final String FORMAT_TYPE = "PNG"; // 二维码图片类型

    /**
     * 默认需要logo,无底部文字
     * 返回 BufferedImage 可以使用ImageIO.write(BufferedImage, "png", outputStream);输出
     *
     * @param dataStr
     * @return 返回 BufferedImage 可以使用ImageIO.write(BufferedImage, "png", outputStream);输出
     */
//    @SneakyThrows
//    public static BufferedImage getQRCodeImage(String dataStr) {
//        return getQRCodeImage(dataStr, true, null);
//    }

    /**
     * 默认需要logo,无底部文字
     *
     * @param dataStr
     * @return 返回字节数组
     */
//    @SneakyThrows
//    public static byte[] getQRCodeByte(String dataStr) {
//        BufferedImage bufferedImage = getQRCodeImage(dataStr, true, null);
//        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//        ImageIO.write(bufferedImage, FORMAT_TYPE, outputStream);
//        return outputStream.toByteArray();
//    }

    /**
     * 默认需要logo，包含底部文字 文字为空则不显示文字
     * 返回 BufferedImage 可以使用ImageIO.write(BufferedImage, "png", outputStream);输出
     *
     * @param dataStr
     * @return
     */
    @SneakyThrows
    public static BufferedImage getQRCodeImage(String dataStr, String bottomText) {
        return getQRCodeImage(dataStr, true, bottomText);
    }

    /**
     * 默认需要logo，包含底部文字 文字为空则不显示文字
     *
     * @param dataStr
     * @return 返回字节数组
     */
    @SneakyThrows
    public static byte[] getQRCodeByte(String dataStr, String bottomText) {
        BufferedImage bufferedImage = getQRCodeImage(dataStr, true, bottomText);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ImageIO.write(bufferedImage, FORMAT_TYPE, outputStream);
        return outputStream.toByteArray();
    }

    /**
     * 获取二维码图片
     *
     * @param dataStr    二维码内容
     * @param needLogo   是否需要添加logo
     * @param bottomText 底部文字       为空则不显示
     * @return
     */
    @SneakyThrows
    public static BufferedImage getQRCodeImage(String dataStr, boolean needLogo, String bottomText) {
        if (dataStr == null) {
            throw new RuntimeException("未包含任何信息");
        }
        HashMap<EncodeHintType, Object> hints = new HashMap<>();
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");    //定义内容字符集的编码
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);        //定义纠错等级
        hints.put(EncodeHintType.MARGIN, 1);
        QRCodeWriter qrCodeWriter = new QRCodeWriter();
        BitMatrix bitMatrix = qrCodeWriter.encode(dataStr, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints);

        int width = bitMatrix.getWidth();
        int height = bitMatrix.getHeight();
        int tempHeight = height;
        if (StringUtils.hasText(bottomText)) {
            tempHeight = tempHeight + 12; // 动态计算图片高度
        }
        BufferedImage image = new BufferedImage(width, tempHeight, BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
            }
        }
        // 判断是否添加logo
        if (needLogo) {
            insertLogoImage(image);
        }
        // 判断是否添加底部文字
        if (StringUtils.hasText(bottomText)) {
            addFontImage(image, bottomText);
//            addTextBelowQRCode(image, bottomText);
        }
        return image;
    }

    /**
     * 插入logo图片
     *
     * @param source 二维码图片
     * @throws Exception
     */
    private static void insertLogoImage(BufferedImage source) throws Exception {
        // 默认logo放于resource/static/image目录下
        ClassPathResource classPathResource = new ClassPathResource("static/image/logo.png");
        InputStream inputStream = classPathResource.getInputStream();
        if (inputStream.available() == 0) {
            return;
        }
        Image src = ImageIO.read(inputStream);
        int width = 30;
        int height = 30;

        Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH);
        BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = tag.getGraphics();
        g.drawImage(image, 0, 0, null); // 绘制缩小后的图
        g.dispose();
        src = image;

        // 插入LOGO
        Graphics2D graph = source.createGraphics();
        int x = (QRCODE_SIZE - width) / 2;
        int y = (QRCODE_SIZE - height) / 2;
        graph.drawImage(src, x, y, width, height, null);
        Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);
        graph.setStroke(new BasicStroke(3f));
        graph.draw(shape);
        graph.dispose();
    }

    private static void addFontImage(BufferedImage source, String declareText) {
        //生成image
        int defineWidth = QRCODE_SIZE;
        int defineHeight = 20;
        BufferedImage textImage = new BufferedImage(defineWidth, defineHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2 = (Graphics2D) textImage.getGraphics();
        //开启文字抗锯齿
        g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,   RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g2.setBackground(Color.WHITE);
        g2.clearRect(0, 0, defineWidth, defineHeight);
        g2.setPaint(Color.BLACK);
        FontRenderContext context = g2.getFontRenderContext();
        //部署linux需要注意 linux无此字体会显示方块
        Font font = new Font("AR", Font.BOLD, 24);
        g2.setFont(font);

        LineMetrics lineMetrics = font.getLineMetrics(declareText, context);
        FontMetrics fontMetrics = FontDesignMetrics.getMetrics(font);
        float offset = (float) (defineWidth - fontMetrics.stringWidth(declareText)) / 2;
        float y = (defineHeight + lineMetrics.getAscent() - lineMetrics.getDescent() - lineMetrics.getLeading()) / 2;

        g2.drawString(declareText, (int) offset, (int) y);
        Graphics2D graph = source.createGraphics();
        //开启文字抗锯齿
        graph.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,   RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        //添加image
        int width = textImage.getWidth(null);
        int height = textImage.getHeight(null);

        Image src = textImage;
        graph.drawImage(src, 0, QRCODE_SIZE - 8, width, height, Color.WHITE, null);
        graph.dispose();
    }

    private static void addTextBelowQRCode(BufferedImage image, String text1) {
        Graphics2D g2d = image.createGraphics();
        g2d.setColor(Color.WHITE);
        int text1Y = image.getHeight() - 10; // Initial position below QR code
        g2d.fillRect(0, text1Y - 12, QRCODE_SIZE, 12);
        g2d.setColor(Color.BLACK);


        // Load a TrueType font that supports Chinese characters
        // 加载支持中文字符的 TrueType 字体，并加粗
        Font font1 = new Font("宋体", Font.BOLD, 24); // text1字体加粗且比text2大
        g2d.setFont(font1);

        // Calculate text1 position and handle text wrapping
        FontMetrics fm1 = g2d.getFontMetrics();
        int maxWidth = 280; // Image width
        java.util.List<String> linesText1 = wrapText(text1, fm1, maxWidth);


        int lineHeight = fm1.getHeight();

        // Draw text1
        for (String line : linesText1) {
            int text1Width = fm1.stringWidth(line);
            int text1X = (image.getWidth() - text1Width) / 2;

            g2d.drawString(line, text1X, text1Y);
            text1Y += lineHeight; // Move to next line (downwards)
        }

        g2d.dispose();
    }

    private static java.util.List<String> wrapText(String text, FontMetrics fm, int maxWidth) {
        java.util.List<String> lines = new ArrayList<>();

        int textWidth = fm.stringWidth(text);
        if (textWidth <= maxWidth) {
            lines.add(text);
            return lines;
        }

        StringBuilder currentLine = new StringBuilder();
        int length = text.length();
        int start = 0;
        int end = 0;

        while (start < length) {
            while (end < length && fm.stringWidth(text.substring(start, end + 1)) <= maxWidth) {
                end++;
            }

            if (end == length) {
                lines.add(text.substring(start));
                break;
            } else {
                int lastSpace = text.substring(start, end).lastIndexOf(" ");
                if (lastSpace != -1 && start + lastSpace < end) {
                    lines.add(text.substring(start, start + lastSpace));
                    start += lastSpace + 1;
                } else {
                    lines.add(text.substring(start, end));
                    start = end;
                }
            }
        }

        return lines;
    }

    @SneakyThrows
    public static BufferedImage getQRCodeImage(String dataStr, boolean needLogo, JSONObject bottomText) {
        if (dataStr == null) {
            throw new RuntimeException("未包含任何信息");
        }
        HashMap<EncodeHintType, Object> hints = new HashMap<>();
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");    //定义内容字符集的编码
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);        //定义纠错等级
        hints.put(EncodeHintType.MARGIN, 1);
        QRCodeWriter qrCodeWriter = new QRCodeWriter();
        BitMatrix bitMatrix = qrCodeWriter.encode(dataStr, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints);

        int width = bitMatrix.getWidth();
        int height = bitMatrix.getHeight();
        int tempHeight = height;
        if (StringUtils.hasText(bottomText.toString())) {
            tempHeight = tempHeight + 240; // 动态计算图片高度
        }
        BufferedImage image = new BufferedImage(width, tempHeight, BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
            }
        }
        // 判断是否添加logo
        if (needLogo) {
            insertLogoImage(image);
        }
        // 判断是否添加底部文字
        if (StringUtils.hasText(bottomText.toString())) {
//            addFontImage(image, bottomText);
            addTextBelowQRCode(image, bottomText);
        }
        return image;
    }

    private static void addTextBelowQRCode(BufferedImage image, JSONObject text1) {
        Graphics2D g2d = image.createGraphics();
        g2d.setColor(Color.WHITE);
        int text1Y = image.getHeight() - 220; // Initial position below QR code
        g2d.fillRect(0, text1Y - 20, QRCODE_SIZE, 240);
        g2d.setColor(Color.BLACK);


        // Load a TrueType font that supports Chinese characters
        // 加载支持中文字符的 TrueType 字体，并加粗
        Font font1 = new Font("宋体", Font.BOLD, 20); // text1字体加粗且比text2大
        g2d.setFont(font1);

        // Calculate text1 position and handle text wrapping
        FontMetrics fm1 = g2d.getFontMetrics();
        List<String> linesText1 = null;
        try {
            linesText1 = wrapText(text1);
        } catch (Exception e) {
            log.error("wrapText方法出现异常： ", e);
            throw new RuntimeException(e);
        }

        int lineHeight = fm1.getHeight();

        // Draw text1
        for (String line : linesText1) {
            int text1Width = fm1.stringWidth(line);
            int text1X = (image.getWidth() - text1Width) / 2;

            g2d.drawString(line, text1X, text1Y);
            text1Y += lineHeight; // Move to next line (downwards)
        }

        g2d.dispose();
    }

    private static java.util.List<String> wrapText(JSONObject jsonObject) {
        java.util.List<String> lines = new ArrayList<>();

        for (String key : jsonObject.keySet()) {
            Object value = jsonObject.get(key);
            String s = String.valueOf(value).replaceAll("[{}\"]", "");
            if (s.length()>14){
                String substring = s.substring(0, 14);
                String substring1 = s.substring(14);
                lines.add(key + ": " + substring);
                lines.add(substring1);
            } else {
                lines.add(key + ": " + s);
            }
        }

        return lines;
    }

}
